/**
 * Copyright 2017 orivil Authors. All rights reserved.
 * Use of this source code is governed by a MIT-style
 * license that can be found at https://mit-license.org.
 */

package session

import (
	"sync"
	"github.com/go-redis/redis"
	"os"
	"io/ioutil"
)

type Storage interface {
	ReadAllIDs() (ids []string, err error)
	ReadSession(id string) (data []byte, err error)
	SaveSession(id string, data []byte) (err error)
	DelSession(id string) (err error)
}

// memory storage 使用内存作为存储器, 当 rpc server 退出时所有数据都被清除
type MemoryStorage struct {
	sessions map[string][]byte
	locker sync.RWMutex
}

func NewMemoryStorage() *MemoryStorage {
	return &MemoryStorage{sessions: make(map[string][]byte, 10000)}
}

func (ms *MemoryStorage) ReadAllIDs() (ids []string, err error) {
	return nil, nil
}

func (ms *MemoryStorage) ReadSession(id string) (data []byte, err error) {
	return ms.sessions[id], nil
}

func (ms *MemoryStorage) SaveSession(id string, data []byte) (err error) {
	ms.sessions[id] = data
	return nil
}

func (ms *MemoryStorage) DelSession(id string) (err error) {
	delete(ms.sessions, id)
	return nil
}

// redis storage 使用 redis 作为存储器, 且内置数据缓存, 读取数据时先从缓存中读取
type RedisStorage struct {
	client *redis.Client
	sessions map[string][]byte
	locker sync.Mutex
}

func NewRedisStorage(client *redis.Client) *RedisStorage {
	return &RedisStorage{client:client}
}

func (rs *RedisStorage) ReadAllIDs() (ids []string, err error) {
	ids, err = rs.client.Keys("").Result()
	if err != nil {
		return nil, err
	} else {
		rs.sessions = make(map[string][]byte, len(ids))
		return ids, nil
	}
}

func (rs *RedisStorage) ReadSession(id string) (data []byte, err error) {
	rs.locker.Lock()
	defer rs.locker.Unlock()
	if session, ok := rs.sessions[id]; ok {
		return session, nil
	} else {
		str, err := rs.client.Get(id).Result()
		if err != nil {
			return nil, err
		} else {
			session = []byte(str)
			rs.sessions[id] = session
			return session, nil
		}
	}
}

func (rs *RedisStorage) SaveSession(id string, data []byte) (err error) {
	rs.locker.Lock()
	defer rs.locker.Unlock()
	rs.sessions[id] = data
	return rs.client.Set(id, string(data), 0).Err()
}

func (rs *RedisStorage) DelSession(id string) (err error) {
	rs.locker.Lock()
	defer rs.locker.Unlock()
	delete(rs.sessions, id)
	return rs.client.Del(id).Err()
}

// file storage 使用硬盘作为存储器, 且内置数据缓存, 读取数据时先从缓存中读取
type FileStorage struct {
	dir string
	sessions map[string][]byte
	locker sync.Mutex
}

func NewFileStorage(dir string) *FileStorage {
	err := os.MkdirAll(dir, 0777)
	if err != nil {
		panic(err)
	}
	return &FileStorage{dir: dir}
}

func (rfs *FileStorage) ReadAllIDs() (ids []string, err error) {
	infos, err := ioutil.ReadDir(rfs.dir)
	if err != nil {
		return nil, err
	} else {
		ids = make([]string, len(infos))
		rfs.sessions = make(map[string][]byte, len(infos))
		for key, info := range infos {
			ids[key] = info.Name()
		}
		return ids, nil
	}
}

func (rfs *FileStorage) ReadSession(id string) (data []byte, err error) {
	rfs.locker.Lock()
	defer rfs.locker.Unlock()
	if session, ok := rfs.sessions[id]; ok {
		return session, nil
	} else {
		session, err = ioutil.ReadFile(filename(rfs.dir, id))
		if err != nil {
			return nil, err
		} else {
			rfs.sessions[id] = session
			return session, nil
		}
	}
}

func (rfs *FileStorage) SaveSession(id string, data []byte) (err error) {
	rfs.locker.Lock()
	defer rfs.locker.Unlock()
	rfs.sessions[id] = data
	return ioutil.WriteFile(filename(rfs.dir, id), data, 0777)
}

func (rfs *FileStorage) DelSession(id string) (err error) {
	rfs.locker.Lock()
	defer rfs.locker.Unlock()
	delete(rfs.sessions, id)
	return os.Remove(filename(rfs.dir, id))
}